use std::time::Duration;

use axum::http::{HeaderName, HeaderValue, Method};
use serde::Deserialize;
use tower_http::cors::CorsLayer;

#[derive(Clone, Deserialize, Debug, Default)]
pub struct CorsConfig {
    #[serde(default)]
    pub allow_origin: String,
    #[serde(default)]
    pub allow_headers: Vec<String>,
    #[serde(default)]
    pub allow_methods: Vec<String>,
    #[serde(default)]
    pub max_age: Option<u64>,
    #[serde(default)]
    pub expose_headers: Vec<String>,
    #[serde(default)]
    pub allow_credentials: Option<bool>,
}

impl CorsConfig {
    pub fn create_cors_layer(&self) -> CorsLayer {
        let layer = CorsLayer::new();
        let layer = match self.max_age {
            Some(age) => layer.max_age(Duration::from_secs(age)),
            _ => layer,
        };
        let layer = match self.allow_origin.is_empty() {
            false => layer.allow_origin(self.allow_origin.parse::<HeaderValue>().unwrap()),
            _ => layer,
        };
        let layer = match self.allow_headers.is_empty() {
            false => layer.allow_headers(
                self.allow_headers
                    .iter()
                    .map(|x| x.parse().unwrap())
                    .collect::<Vec<HeaderName>>(),
            ),
            _ => layer.allow_headers([
                "cache-control".parse().unwrap(),
                "x-requested-with".parse().unwrap(),
            ]),
        };
        let layer = match self.expose_headers.is_empty() {
            false => layer.expose_headers(
                self.expose_headers
                    .iter()
                    .map(|x| x.parse().unwrap())
                    .collect::<Vec<HeaderName>>(),
            ),
            _ => layer,
        };
        let layer = match self.allow_methods.is_empty() {
            false => layer.allow_methods(
                self.allow_methods
                    .iter()
                    .map(|x| x.parse().unwrap())
                    .collect::<Vec<Method>>(),
            ),
            _ => layer.allow_methods([
                Method::GET,
                Method::PUT,
                Method::POST,
                Method::DELETE,
                Method::OPTIONS,
            ]),
        };
        let layer = match self.allow_credentials {
            Some(x) => layer.allow_credentials(x),
            _ => layer,
        };
        layer
    }
}
